<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
function get_siteroot()
{
    return 'http://' . $_SERVER['HTTP_HOST'] . __ROOT__;
}

/**
 * Think 基础函数库
 * @category   Think
 * @package  Common
 * @author   liu21st <liu21st@gmail.com>
 */

/**
 * 记录和统计时间（微秒）和内存使用情况
 * 使用方法:
 * <code>
 * G('begin'); // 记录开始标记位
 * // ... 区间运行代码
 * G('end'); // 记录结束标签位
 * echo G('begin','end',6); // 统计区间运行时间 精确到小数后6位
 * echo G('begin','end','m'); // 统计区间内存使用情况
 * 如果end标记位没有定义，则会自动以当前作为标记位
 * 其中统计内存使用需要 MEMORY_LIMIT_ON 常量为true才有效
 * </code>
 * @param string $start 开始标签
 * @param string $end 结束标签
 * @param integer|string $dec 小数位或者m
 * @return mixed
 */
function G($start, $end = '', $dec = 4)
{
    static $_info = array();
    static $_mem = array();
    if (is_float($end)) { // 记录时间
        $_info[$start] = $end;
    }
    elseif (!empty($end)) { // 统计时间和内存使用
        if (!isset($_info[$end])) {
            $_info[$end] = microtime(TRUE);
        }
        if (MEMORY_LIMIT_ON && $dec == 'm') {
            if (!isset($_mem[$end])) {
                $_mem[$end] = memory_get_usage();
            }
            // number_format() 函数通过千位分组来格式化数字。
            return number_format(($_mem[$end] - $_mem[$start]) / 1024);
        }
        else {
            return number_format(($_info[$end] - $_info[$start]), $dec);
        }

    }
    else { // 记录时间和内存使用
        $_info[$start] = microtime(TRUE);
        if (MEMORY_LIMIT_ON) {
            $_mem[$start] = memory_get_usage();
        }
    }
}

/**
 * 设置和获取统计数据
 * 使用方法:
 * <code>
 * N('db',1); // 记录数据库操作次数
 * N('read',1); // 记录读取次数
 * echo N('db'); // 获取当前页面数据库的所有操作次数
 * echo N('read'); // 获取当前页面读取次数
 * </code>
 * @param string $key 标识位置
 * @param integer $step 步进值
 * @return mixed
 */
function N($key, $step = 0)
{
    static $_num = array();
    if (!isset($_num[$key])) {
        $_num[$key] = 0;
    }
    if (empty($step)) {
        return $_num[$key];
    }
    else {
        $_num[$key] = $_num[$key] + (int)$step;
    }
}

/**
 * 字符串命名风格转换
 * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
 * @param string $name 字符串
 * @param integer $type 转换类型
 * @return string
 */
function parse_name($name, $type = 0)
{
    if ($type) {
        return ucfirst(preg_replace("/_([a-zA-Z])/e", "strtoupper('\\1')", $name));
    }
    else {
        return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
    }
}

/**
 * 优化的require_once
 * @param string $filename 文件地址
 * @return boolen
 */
function require_cache($filename)
{
    static $_importFiles = array();
    if (!isset($_importFiles[$filename])) {
        if (file_exists_case($filename)) {
            require $filename;
            $_importFiles[$filename] = true;
        }
        else {
            $_importFiles[$filename] = false;
        }
    }
    return $_importFiles[$filename];
}

/**
 * 区分大小写的文件存在判断
 * @param string $filename 文件地址
 * @return boolen
 */
function file_exists_case($filename)
{
    if (is_file($filename)) {
        if (IS_WIN && C('APP_FILE_CASE')) {
            if (basename(realpath($filename)) != basename($filename)) {
                return false;
            }
        }
        return true;
    }
    return false;
}

/**
 * 导入所需的类库 同java的Import 本函数有缓存功能
 * @param string $class 类库命名空间字符串
 * @param string $baseUrl 起始路径
 * @param string $ext 导入的文件扩展名
 * @return boolen
 */
function import($class, $baseUrl = '', $ext = '.class.php')
{
    static $_file = array();
    $class = str_replace(array('.', '#'), array('/', '.'), $class);
    if ('' === $baseUrl && false === strpos($class, '/')) {
        // 检查别名导入
        return alias_import($class);
    }
    if (isset($_file[$class . $baseUrl])) {
        return true;
    }
    else {
        $_file[$class . $baseUrl] = true;
    }

    $class_strut = explode('/', $class);
    if (empty($baseUrl)) {
        if ('@' == $class_strut[0] || APP_NAME == $class_strut[0]) {
            //加载当前项目应用类库
            $baseUrl = dirname(LIB_PATH);
            $class   = substr_replace($class, basename(LIB_PATH) . '/', 0, strlen($class_strut[0]) + 1);

            if ($class_strut[1] == 'Model') {
                $model_name        = $class_strut[2];
                $model_path        = dirname($baseUrl . '/' . $class);
                $model_file        = $model_path . '/' . GROUP_NAME . '/' . $model_name . '.class.php';
                $global_model_file = $model_path . '/' . $model_name . '.class.php';

                if (!file_exists($global_model_file) && !file_exists($model_file)) {
                    if (!file_exists($model_path . '/' . GROUP_NAME)) {
                        @mkdir($model_path . '/' . GROUP_NAME);
                    }
                    $content = <<<EOF
<?php
class $model_name extends baseModel
{
    protected function _parse_item(\$result, \$_options = [])
    {
        return parent::_parse_item(\$result, \$_options);
    }
}
EOF;
                    file_put_contents($model_file, $content);
                }

                if (file_exists($model_file)) {
                    $class = $model_file;
                }
            }
        }
        elseif ('think' == strtolower($class_strut[0])) { // think 官方基类库
            $baseUrl = CORE_PATH;
            $class   = substr($class, 6);
        }
        elseif (in_array(strtolower($class_strut[0]), array('org', 'com'))) {
            // org 第三方公共类库 com 企业公共类库
            $baseUrl = LIBRARY_PATH;
        }
        else { // 加载其他项目应用类库
            $class   = substr_replace($class, '', 0, strlen($class_strut[0]) + 1);
            $baseUrl = APP_PATH . '../' . $class_strut[0] . '/' . basename(LIB_PATH) . '/';
        }
    }
    if (substr($baseUrl, -1) != '/') {
        $baseUrl .= '/';
    }
    $classfile = $baseUrl . $class . $ext;
    if (!class_exists(basename($class), false)) {
        // 如果类不存在 则导入类库文件
        return require_cache($classfile);
    }
}

/**
 * 基于命名空间方式导入函数库
 * load('@.Util.Array')
 * @param string $name 函数库命名空间字符串
 * @param string $baseUrl 起始路径
 * @param string $ext 导入的文件扩展名
 * @return void
 */
function load($name, $baseUrl = '', $ext = '.php')
{
    $name = str_replace(array('.', '#'), array('/', '.'), $name);
    if (empty($baseUrl)) {
        if (0 === strpos($name, '@/')) {
            //加载当前项目函数库
            $baseUrl = COMMON_PATH;
            $name    = substr($name, 2);
        }
        else {
            //加载ThinkPHP 系统函数库
            $baseUrl = EXTEND_PATH . 'Function/';
        }
    }
    if (substr($baseUrl, -1) != '/') {
        $baseUrl .= '/';
    }
    require_cache($baseUrl . $name . $ext);
}

/**
 * 快速导入第三方框架类库 所有第三方框架的类库文件统一放到 系统的Vendor目录下面
 * @param string $class 类库
 * @param string $baseUrl 基础目录
 * @param string $ext 类库后缀
 * @return boolean
 */
function vendor($class, $baseUrl = '', $ext = '.php')
{
    if (empty($baseUrl)) {
        $baseUrl = VENDOR_PATH;
    }
    return import($class, $baseUrl, $ext);
}

/**
 * 快速定义和导入别名 支持批量定义
 * @param string|array $alias 类库别名
 * @param string $classfile 对应类库
 * @return boolean
 */
function alias_import($alias, $classfile = '')
{
    static $_alias = array();
    if (is_string($alias)) {
        if (isset($_alias[$alias])) {
            return require_cache($_alias[$alias]);
        }
        elseif ('' !== $classfile) {
            // 定义别名导入
            $_alias[$alias] = $classfile;
            return;
        }
    }
    elseif (is_array($alias)) {
        $_alias = array_merge($_alias, $alias);
        return;
    }
    return false;
}

/**
 * D函数用于实例化Model 格式 项目://分组/模块
 * @param string $name Model资源地址
 * @param string $layer 业务层名称
 * @return Model
 */
function D($name = '', $layer = '')
{
    if (empty($name)) {
        return new Model;
    }
    static $_model = array();
    $layer = $layer ? $layer : C('DEFAULT_M_LAYER');
    if (strpos($name, '://')) {// 指定项目
        $name = str_replace('://', '/' . $layer . '/', $name);
    }
    else {
        $name = C('DEFAULT_APP') . '/' . $layer . '/' . $name;
    }
    if (isset($_model[$name])) {
        return $_model[$name];
    }
    import($name . $layer);
    $class = basename($name . $layer);

    if (class_exists($class)) {
        $model = new $class();
    }
    else {
        $model = new Model(basename($name));
    }
    $_model[$name] = $model;
    return $model;
}

/**
 * M函数用于实例化一个没有模型文件的Model
 * @param string $name Model名称 支持指定基础模型 例如 MongoModel:User
 * @param string $tablePrefix 表前缀
 * @param mixed $connection 数据库连接信息
 * @return Model
 */
function M($name = '', $tablePrefix = '', $connection = '')
{
    static $_model = array();
    if (strpos($name, ':')) {
        list($class, $name) = explode(':', $name);
    }
    else {
        $class = 'Model';
    }
    $guid = $tablePrefix . $name . '_' . $class;
    if (!isset($_model[$guid])) {
        $_model[$guid] = new $class($name, $tablePrefix, $connection);
    }
    return $_model[$guid];
}

/**
 * A函数用于实例化Action 格式：[项目://][分组/]模块
 * @param string $name Action资源地址
 * @param string $layer 控制层名称
 * @return Action|false
 */
function A($name, $layer = '')
{
    static $_action = array();
    $layer = $layer ? $layer : C('DEFAULT_C_LAYER');
    if (strpos($name, '://')) {// 指定项目
        $name = str_replace('://', '/' . $layer . '/', $name);
    }
    else {
        $name = '@/' . $layer . '/' . $name;
    }
    if (isset($_action[$name])) {
        return $_action[$name];
    }
    import($name . $layer);
    $class = basename($name . $layer);
    if (class_exists($class, false)) {
        $action         = new $class();
        $_action[$name] = $action;
        return $action;
    }
    else {
        return false;
    }
}

/**
 * 远程调用模块的操作方法 URL 参数格式 [项目://][分组/]模块/操作
 * @param string $url 调用地址
 * @param string|array $vars 调用参数 支持字符串和数组
 * @param string $layer 要调用的控制层名称
 * @return mixed
 */
function R($url, $vars = array(), $layer = '')
{
    $info   = pathinfo($url);
    $action = $info['basename'];
    $module = $info['dirname'];
    $class  = A($module, $layer);
    if ($class) {
        if (is_string($vars)) {
            parse_str($vars, $vars);
        }
        return call_user_func_array(array(&$class, $action), $vars);
    }
    else {
        return false;
    }
}

/**
 * 获取和设置语言定义(不区分大小写)
 * @param string|array $name 语言变量
 * @param string $value 语言值
 * @return mixed
 */
function L($name = null, $value = null)
{
    if (is_string($name)) {
        if (mb_strlen($name, 'utf-8') != strlen($name)) {
            return $name;
        }
    }
    static $_lang = array();
    // 空参数返回所有定义
    if (empty($name)) {
        return $_lang;
    }
    // 判断语言获取(或设置)
    // 若不存在,直接返回全大写$name
    if (is_string($name)) {
        $name = strtoupper($name);
        if (is_null($value)) {
            return isset($_lang[$name]) ? $_lang[$name] : $name;
        }
        $_lang[$name] = $value; // 语言定义
        return;
    }
    // 批量定义
    if (is_array($name)) {
        $_lang = array_merge($_lang, array_change_key_case($name, CASE_UPPER));
    }
    return;
}

/**
 * 获取和设置配置参数 支持批量定义
 * @param string|array $name 配置变量
 * @param mixed $value 配置值
 * @return mixed
 */
function C($name = null, $value = null)
{
    static $_config = array();
    // 无参数时获取所有
    if (empty($name)) {
        if (!empty($value) && $array = cache('c_' . $value)) {
            $_config = array_merge($_config, array_change_key_case($array));
        }
        return $_config;
    }
    // 优先执行设置获取或赋值
    if (is_string($name)) {
        if (!strpos($name, '.')) {
            $name = strtolower($name);
            if (is_null($value)) {
                return isset($_config[$name]) ? $_config[$name] : null;
            }
            $_config[$name] = $value;
            return;
        }
        // 二维数组设置和获取支持
        $name    = explode('.', $name);
        $name[0] = strtolower($name[0]);
        if (is_null($value)) {
            return isset($_config[$name[0]][$name[1]]) ? $_config[$name[0]][$name[1]] : null;
        }
        $_config[$name[0]][$name[1]] = $value;
        return;
    }
    // 批量设置
    if (is_array($name)) {
        $_config = array_merge($_config, array_change_key_case($name));
        if (!empty($value)) {// 保存配置值
            cache('c_' . $value, $_config);
        }
        return;
    }
    return null; // 避免非法参数
}

/**
 * 处理标签扩展
 * @param string $tag 标签名称
 * @param mixed $params 传入参数
 * @return mixed
 */
 /*
 * 记录和统计时间（微秒）和内存使用情况
 * 使用方法:
 * <code>
 * G('begin'); // 记录开始标记位
 * // ... 区间运行代码
 * G('end'); // 记录结束标签位
 * echo G('begin','end',6); // 统计区间运行时间 精确到小数后6位
 * echo G('begin','end','m'); // 统计区间内存使用情况
 * 如果end标记位没有定义，则会自动以当前作为标记位
 * 其中统计内存使用需要 MEMORY_LIMIT_ON 常量为true才有效
 * </code>
 * @param string $start 开始标签
 * @param string $end 结束标签
 * @param integer|string $dec 小数位或者m
 */
function tag($tag, &$params = NULL)  // $tag--标签名称 &$params--传入参数
{
    // 系统标签扩展
    $extends = C('extends.' . $tag);
    // 应用标签扩展
    $tags = C('tags.' . $tag);
    if (!empty($tags)) {
        if (empty($tags['_overlay']) && !empty($extends)) { // 合并扩展
            $tags = array_unique(array_merge($extends, $tags)); // array_unique 移除数组中重复的值
        }
        elseif (isset($tags['_overlay'])) { // 通过设置 '_overlay'=>1 覆盖系统标签
            unset($tags['_overlay']);
        }
    }
    elseif (!empty($extends)) {
        $tags = $extends;
    }
    if ($tags) {
        if (APP_DEBUG) {
            G($tag . 'Start');
            trace('[ ' . $tag . ' ] --START--', '', 'INFO');
        }
        // 执行扩展
        foreach ($tags as $key => $name) {
            if (!is_int($key)) { // 指定行为类的完整路径 用于模式扩展
                $name = $key;
            }
            B($name, $params);
        }
        if (APP_DEBUG) { // 记录行为的执行日志
            trace('[ ' . $tag . ' ] --END-- [ RunTime:' . G($tag . 'Start', $tag . 'End', 6) . 's ]', '', 'INFO');
        }
    }
    else { // 未执行任何行为 返回false
        return false;
    }
}

/**
 * 动态添加行为扩展到某个标签
 * @param string $tag 标签名称
 * @param string $behavior 行为名称
 * @param string $path 行为路径
 * @return void
 */
function add_tag_behavior($tag, $behavior, $path = '')
{
    $array = C('tags.' . $tag);
    if (!$array) {
        $array = array();
    }
    if ($path) {
        $array[$behavior] = $path;
    }
    else {
        $array[] = $behavior;
    }
    C('tags.' . $tag, $array);
}

/**
 * 过滤器方法 引用传值
 * @param string $name 过滤器名称
 * @param string $content 要过滤的内容
 * @return void
 */
function filter($name, &$content)
{
    $class = $name . 'Filter';
    require_cache(LIB_PATH . 'Filter/' . $class . '.class.php');
    $filter  = new $class();
    $content = $filter->run($content);
}

/**
 * 执行某个行为
 * @param string $name 行为名称
 * @param Mixed $params 传人的参数
 * @return void
 */
function B($name, &$params = NULL)
{
    $class = $name . 'Behavior';
    G('behaviorStart');
    $behavior = new $class();
    $behavior->run($params);
    if (APP_DEBUG) { // 记录行为的执行日志
        trace('Run ' . $name . ' Behavior [ RunTime:' . G('behaviorStart', 'behaviorEnd', 6) . 's ]', '', 'INFO');
    }
}

/**
 * 渲染输出Widget
 * @param string $name Widget名称
 * @param array $data 传人的参数
 * @param boolean $return 是否返回内容
 * @return void
 */
function W($name, $data = array(), $return = false)
{
    $class = $name . 'Widget';
    require_cache(LIB_PATH . 'Widget/' . $class . '.class.php');
    if (!class_exists($class)) {
        throw_exception(L('_CLASS_NOT_EXIST_') . ':' . $class);
    }
    $widget  = Think::instance($class);
    $content = $widget->render($data);
    if ($return) {
        return $content;
    }
    else {
        echo $content;
    }
}

/**
 * 去除代码中的空白和注释
 * @param string $content 代码内容
 * @return string
 */
function strip_whitespace($content)
{
    $stripStr = '';
    //分析php源码
    $tokens     = token_get_all($content);
    $last_space = false;
    for ($i = 0, $j = count($tokens); $i < $j; $i++) {
        if (is_string($tokens[$i])) {
            $last_space = false;
            $stripStr .= $tokens[$i];
        }
        else {
            switch ($tokens[$i][0]) {
                //过滤各种PHP注释
                case T_COMMENT:
                case T_DOC_COMMENT:
                    break;
                //过滤空格
                case T_WHITESPACE:
                    if (!$last_space) {
                        $stripStr .= ' ';
                        $last_space = true;
                    }
                    break;
                case T_START_HEREDOC:
                    $stripStr .= "<<<THINK\n";
                    break;
                case T_END_HEREDOC:
                    $stripStr .= "THINK;\n";
                    for ($k = $i + 1; $k < $j; $k++) {
                        if (is_string($tokens[$k]) && $tokens[$k] == ';') {
                            $i = $k;
                            break;
                        }
                        else if ($tokens[$k][0] == T_CLOSE_TAG) {
                            break;
                        }
                    }
                    break;
                default:
                    $last_space = false;
                    $stripStr .= $tokens[$i][1];
            }
        }
    }
    return $stripStr;
}

//[RUNTIME]
// 编译文件
function compile($filename)
{
    $content = file_get_contents($filename);
    // 替换预编译指令
    $content = preg_replace('/\/\/\[RUNTIME\](.*?)\/\/\[\/RUNTIME\]/s', '', $content);
    $content = substr(trim($content), 5);
    if ('?>' == substr($content, -2)) {
        $content = substr($content, 0, -2);
    }
    return $content;
}

// 根据数组生成常量定义
function array_define($array, $check = true)
{
    $content = "\n";
    foreach ($array as $key => $val) {
        $key = strtoupper($key);
        if ($check) {
            $content .= 'defined(\'' . $key . '\') or ';
        }
        if (is_int($val) || is_float($val)) {
            $content .= "define('" . $key . "'," . $val . ');';
        }
        elseif (is_bool($val)) {
            $val = ($val) ? 'true' : 'false';
            $content .= "define('" . $key . "'," . $val . ');';
        }
        elseif (is_string($val)) {
            $content .= "define('" . $key . "','" . addslashes($val) . "');";
        }
        $content .= "\n";
    }
    return $content;
}

//[/RUNTIME]

/**
 * 添加和获取页面Trace记录
 * @param string $value 变量
 * @param string $label 标签
 * @param string $level 日志级别
 * @return void
 */
function trace($value = '[think]', $label = '', $level = 'DEBUG', $record = false)
{
    static $_trace = array();
    if ('[think]' === $value) { // 获取trace信息
        return $_trace;
    }
    else {
        $info = ($label ? $label . ':' : '') . print_r($value, true);
        if (APP_DEBUG && 'ERR' == $level) {// 调试模式ERR抛出异常
            throw_exception($info);
        }
        if (!isset($_trace[$level])) {
            $_trace[$level] = array();
        }
        $_trace[$level][] = $info;
        if ((defined('IS_AJAX') && IS_AJAX) || !C('SHOW_PAGE_TRACE') || $record) {
            Log::record($info, $level, $record);
        }
    }
}

function decodeUnicode($str)
{
    return preg_replace_callback('/\\\\u([0-9a-f]{4})/i',
        create_function(
            '$matches',
            'return mb_convert_encoding(pack("H*", $matches[1]), "UTF-8", "UCS-2BE");'
        ),
        $str);
}

function jsonPretty($json)
{
    $result          = '';
    $level           = 0;
    $in_quotes       = false;
    $in_escape       = false;
    $ends_line_level = NULL;
    $json_length     = strlen($json);

    for ($i = 0; $i < $json_length; $i++) {
        $char           = $json[$i];
        $new_line_level = NULL;
        $post           = "";
        if ($ends_line_level !== NULL) {
            $new_line_level  = $ends_line_level;
            $ends_line_level = NULL;
        }
        if ($in_escape) {
            $in_escape = false;
        }
        else if ($char === '"') {
            $in_quotes = !$in_quotes;
        }
        else if (!$in_quotes) {
            switch ($char) {
                case '}':
                case ']':
                    $level--;
                    $ends_line_level = NULL;
                    $new_line_level  = $level;
                    break;

                case '{':
                case '[':
                    $level++;
                case ',':
                    $ends_line_level = $level;
                    break;

                case ':':
                    $post = " ";
                    break;

                case " ":
                case "\t":
                case "\n":
                case "\r":
                    $char            = "";
                    $ends_line_level = $new_line_level;
                    $new_line_level  = NULL;
                    break;
            }
        }
        else if ($char === '\\') {
            $in_escape = true;
        }
        if ($new_line_level !== NULL) {
            $result .= "\n" . str_repeat("\t", $new_line_level);
        }
        $result .= $char . $post;
    }

    return $result;
}